/**
 * $version:  0.1 
 * $Date: 2011-01-21 
 *
 * Copyright (C) 2010-2011 Jawa Software. All rights reserved.
 *
 */

package org.jawa.util;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.jawa.database.DbConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 工具类
 * 
 * @author Y-N
 */
public class JawaGlobals {

	private static final Logger Log = LoggerFactory
			.getLogger(JawaGlobals.class);

	// 系统工作目录
	private static String home = null;

	private static String CONFIG_FILENAME = JawaConstants.CONFIG_FILENAME;

	public static boolean failedLoading = false;

	// 系统的 XML配制文件.
	private static XMLProperties xmlProperties = null;

	private static JawaProperties properties = null;

	/**
	 * 返回系统工作的目录.
	 * 
	 * @return 系统工作的目录.
	 */
	public static String getHomeDirectory() {
		return home;
	}

	/**
	 * 设置系统工作的目录,系统对此目录需要有读与写的权限.
	 * 
	 * @param pathname
	 *            系统工作的目录.
	 */
	public static void setHomeDirectory(String pathname) {
		File mh = new File(pathname);
		if (!mh.exists()) {
			Log.error("错误 - 指定的目录不存在 (" + pathname + ")");
		} else if (!mh.canRead() || !mh.canWrite()) {
			Log.error("错误 - 启动系统的用户对目录没有读或写的权限 " + "(" + pathname + "). "
					+ "请将读和写的权限赋予启动系统的用户.");
		} else {
			home = pathname;
		}
	}

	/**
	 * 返回配制文件中的一个元素值。
	 * <p>
	 * 属性配制文件在系统CLASS目录下,以 <tt>CONFIG_FILENAME</tt>为名称的文件中。
	 * <p>
	 * 元素 "foo.bar.prop" 在 XML 文件中定义如下所示:
	 * 
	 * <pre>
	 * &lt;foo&gt;
	 *     &lt;bar&gt;
	 *         &lt;prop&gt;some value&lt;/prop&gt;
	 *     &lt;/bar&gt;
	 * &lt;/foo&gt;
	 * </pre>
	 * 
	 * 通过getProperties("foo.bar.prop")方法将返回"some value"。 如果同样名称的元素存在多个，将只返回第一个。
	 * 
	 * @param name
	 *            元素名称.
	 * @return 元素值.
	 */
	public static String getXMLProperty(String name) {
		if (xmlProperties == null) {
			loadSetupProperties();
		}

		if (xmlProperties == null) {
			return null;
		}

		return xmlProperties.getProperty(name);
	}

	/**
	 * 返回配制文件中的一个元素值。
	 * <p>
	 * 属性配制文件在系统CLASS目录下,以 <tt>CONFIG_FILENAME</tt>为名称的文件中。
	 * <p>
	 * 元素 "foo.bar.prop" 在 XML 文件中定义如下所示:
	 * 
	 * <pre>
	 * &lt;foo&gt;
	 *     &lt;bar&gt;
	 *         &lt;prop&gt;some value&lt;/prop&gt;
	 *     &lt;/bar&gt;
	 * &lt;/foo&gt;
	 * </pre>
	 * 
	 * 如果指定的元素不存在，将返回默认值。
	 * 
	 * @param name
	 *            元素名称.
	 * @param defaultValue
	 *            默认值.
	 * @return 元素值.
	 */
	public static String getXMLProperty(String name, String defaultValue) {
		if (xmlProperties == null) {
			loadSetupProperties();
		}

		if (xmlProperties == null) {
			return null;
		}

		String value = xmlProperties.getProperty(name);
		if (value == null) {
			value = defaultValue;
		}
		return value;
	}

	/**
	 * 返回配制文件中的一个元素值。
	 * <p>
	 * 属性配制文件在系统CLASS目录下,以 <tt>CONFIG_FILENAME</tt>为名称的文件中。
	 * <p>
	 * 元素 "foo.bar.prop" 在 XML 文件中定义如下所示:
	 * 
	 * <pre>
	 * &lt;foo&gt;
	 *     &lt;bar&gt;
	 *         &lt;prop&gt;some value&lt;/prop&gt;
	 *     &lt;/bar&gt;
	 * &lt;/foo&gt;
	 * </pre>
	 * 
	 * 如果指定的元素值不存在，将返回默认值。
	 * 
	 * @param name
	 *            元素名称.
	 * @param defaultValue
	 *            默认值.
	 * @return 元素值.
	 */
	public static int getXMLProperty(String name, int defaultValue) {
		String value = getXMLProperty(name);
		if (value != null) {
			try {
				return Integer.parseInt(value);
			} catch (NumberFormatException nfe) {
				// 忽略.
			}
		}
		return defaultValue;
	}

	/**
	 * 返回配制文件中的一个元素值。
	 * <p>
	 * 属性配制文件在系统CLASS目录下,以 <tt>CONFIG_FILENAME</tt>为名称的文件中。
	 * <p>
	 * 元素 "foo.bar.prop" 在 XML 文件中定义如下所示:
	 * 
	 * <pre>
	 * &lt;foo&gt;
	 *     &lt;bar&gt;
	 *         &lt;prop&gt;some value&lt;/prop&gt;
	 *     &lt;/bar&gt;
	 * &lt;/foo&gt;
	 * </pre>
	 * 
	 * 如果指定的元素不存在，将返回默认值。
	 * 
	 * @param name
	 *            元素名称.
	 * @param defaultValue
	 *            默认值.
	 * @return 元素值.
	 */
	public static boolean getXMLProperty(String name, boolean defaultValue) {
		String value = getXMLProperty(name);
		if (value != null) {
			return Boolean.valueOf(value);
		}
		return defaultValue;
	}

	/**
	 * 
	 * 返回指定元素名称的所有的元素值列表。
	 * <p>
	 * 属性配制文件在系统CLASS目录下,以 <tt>CONFIG_FILENAME</tt>为名称的文件中。 元素在 XML 文件中定义如下所示:
	 * </p>
	 * 
	 * <pre>
	 * &lt;foo&gt;
	 *     &lt;bar&gt;
	 *         &lt;prop&gt;some value&lt;/prop&gt;
	 *         &lt;prop&gt;other value&lt;/prop&gt;
	 *         &lt;prop&gt;last value&lt;/prop&gt;
	 *     &lt;/bar&gt;
	 * &lt;/foo&gt;
	 * </pre>
	 * 
	 * 通过getProperties("foo.bar.prop")方法将返回{"some value",
	 * "other value","last value"}。
	 * 
	 * 
	 * @param name
	 *            元素名称
	 * @return 所有元素值
	 */
	public static String[] getXMLPropertys(String name) {
		if (xmlProperties == null) {
			loadSetupProperties();
		}

		if (xmlProperties == null) {
			return null;
		}

		return xmlProperties.getProperties(name);
	}

	/**
	 * 返回父元素名称下所有的子元素值列表,如果没有找到则返回一个空列表。
	 * <p>
	 * 假如存以以下元素 <tt>X.Y.A</tt>, <tt>X.Y.B</tt> , <tt>X.Y.C</tt> ,
	 * <tt>X.Y.C.D</tt> , 当通过元素名称 <tt>X.Y</tt> 获取元素值列表时将返回 <tt>A</tt>,
	 * <tt>B</tt>, <tt>C</tt>。 对于<tt>C.D</tt>将不会返回。
	 * <p>
	 * <p>
	 * 属性配制文件在系统CLASS目录下,以 <tt>CONFIG_FILENAME</tt> 为名称的文件中。
	 * <p>
	 * 元素 "foo.bar.prop" 在 XML 文件中定义如下所示:
	 * 
	 * <pre>
	 * &lt;foo&gt;
	 *     &lt;bar&gt;
	 *         &lt;prop&gt;some value&lt;/prop&gt;
	 *     &lt;/bar&gt;
	 * &lt;/foo&gt;
	 * </pre>
	 * 
	 * 
	 * @param parent
	 *            父元素名称.
	 * @return 元素值列表.
	 */
	public static List getChildrenXMLProperties(String parent) {
		if (xmlProperties == null) {
			loadSetupProperties();
		}

		if (xmlProperties == null) {
			return Collections.EMPTY_LIST;
		}

		String[] propNames = xmlProperties.getChildrenPropertieNames(parent);
		List<String> values = new ArrayList<String>();
		for (String propName : propNames) {
			String value = getXMLProperty(parent + "." + propName);
			if (value != null) {
				values.add(value);
			}
		}

		return values;
	}

	/**
	 * 设置元素值。如果指定的元素不存在，将创建一个新的元素。
	 * <p>
	 * 属性配制文件在系统CLASS目录下,以 <tt>CONFIG_FILENAME</tt>为名称的文件中。
	 * <p>
	 * 
	 * 元素 "foo.bar.prop" 在 XML 文件中定义如下所示:
	 * 
	 * <pre>
	 * &lt;foo&gt;
	 *     &lt;bar&gt;
	 *         &lt;prop&gt;some value&lt;/prop&gt;
	 *     &lt;/bar&gt;
	 * &lt;/foo&gt;
	 * </pre>
	 * 
	 * @param name
	 *            元素名称.
	 * @param value
	 *            元素值.
	 */
	public static void setXMLProperty(String name, String value) {
		if (xmlProperties == null) {
			loadSetupProperties();
		}

		if (xmlProperties != null) {
			xmlProperties.setProperty(name, value);
		}
	}

	/**
	 * 删除一个元素。如果对应的元素不存在，则什么也不做。
	 * 
	 * @param name
	 *            元素名称.
	 */
	public static void deleteXMLProperty(String name) {
		if (xmlProperties == null) {
			loadSetupProperties();
		}
		xmlProperties.deleteProperty(name);
	}

	/**
	 * 返回一个属性.
	 * 
	 * @param name
	 *            属性名称.
	 * @return 属性值.
	 */
	public static String getProperty(String name) {
		if (properties == null) {
			if (isSetupMode()) {
				return null;
			}
			properties = JawaProperties.getInstance();
		}
		return properties.get(name);
	}

	/**
	 * 
	 * 返回一个属性值,如果指定的属性不存在返回默认值.
	 * 
	 * @param name
	 *            属性名称.
	 * @param defaultValue
	 *            如果指定的属性不存在,返回的默认值.
	 * @return 属性值.
	 */
	public static String getProperty(String name, String defaultValue) {
		if (properties == null) {
			if (isSetupMode()) {
				return defaultValue;
			}
			properties = JawaProperties.getInstance();
		}
		String value = properties.get(name);
		if (value != null) {
			return value;
		} else {
			return defaultValue;
		}
	}

	/**
	 * 返回一个属性值,如果指定的属性不存在返回默认值.
	 * 
	 * @param name
	 *            属性名称.
	 * @param defaultValue
	 *            如果指定的属性不存在,返回的默认值.
	 * @return 属性值.
	 */
	public static int getIntProperty(String name, int defaultValue) {
		String value = getProperty(name);
		if (value != null) {
			try {
				return Integer.parseInt(value);
			} catch (NumberFormatException nfe) {
				// 忽略.
			}
		}
		return defaultValue;
	}

	/**
	 * 返回一个属性值,如果指定的属性不存在返回默认值.
	 * 
	 * @param name
	 *            属性名称.
	 * @param defaultValue
	 *            如果指定的属性不存在,返回的默认值.
	 * @return 属性值.
	 */
	public static long getLongProperty(String name, long defaultValue) {
		String value = getProperty(name);
		if (value != null) {
			try {
				return Long.parseLong(value);
			} catch (NumberFormatException nfe) {
				// Ignore.
			}
		}
		return defaultValue;
	}

	/**
	 * 返回一个属性值.
	 * 
	 * @param name
	 *            属性名称.
	 * @return 属性值.
	 */
	public static boolean getBooleanProperty(String name) {
		return Boolean.valueOf(getProperty(name));
	}

	/**
	 * 返回一个属性值,如果指定的属性不存在返回默认值.
	 * 
	 * 
	 * @param name
	 *            属性名称.
	 * @param defaultValue
	 *            value returned if the property doesn't exist.
	 * @return true if the property value exists and is set to <tt>"true"</tt>
	 *         (ignoring case). Otherwise <tt>false</tt> is returned.
	 */
	public static boolean getBooleanProperty(String name, boolean defaultValue) {
		String value = getProperty(name);
		if (value != null) {
			return Boolean.valueOf(value);
		} else {
			return defaultValue;
		}
	}

	/**
	 * 返回父属性下的所有子属性名称. 例如存在以下属性 <tt>X.Y.A</tt>, <tt>X.Y.B</tt>, and
	 * <tt>X.Y.C</tt> , 调用 <tt>X.Y</tt> 将返回<tt>X.Y.A</tt>, <tt>X.Y.B</tt>, and
	 * <tt>X.Y.C</tt>. 但是本属性不会返回子属性的子属性名称.
	 * 
	 * @param parent
	 *            父属性名称.
	 * @return 子属性名称列表.
	 */
	public static List<String> getPropertyNames(String parent) {
		if (properties == null) {
			if (isSetupMode()) {
				return new ArrayList<String>();
			}
			properties = JawaProperties.getInstance();
		}
		return new ArrayList<String>(properties.getChildrenNames(parent));
	}

	/**
	 * 返回父属性下的所有子属性值. 例如存在以下属性 <tt>X.Y.A</tt>, <tt>X.Y.B</tt>, and
	 * <tt>X.Y.C</tt> , 调用 <tt>X.Y</tt> 将返回<tt>X.Y.A</tt>, <tt>X.Y.B</tt>, 以及
	 * <tt>X.Y.C</tt>的属性值. 但是本属性不会返回子属性的子属性.
	 * 
	 * @param parent
	 *            父属性名称.
	 * @return 隶属于父属性的子属性的值.
	 */
	public static List<String> getProperties(String parent) {
		if (properties == null) {
			if (isSetupMode()) {
				return new ArrayList<String>();
			}
			properties = JawaProperties.getInstance();
		}

		Collection<String> propertyNames = properties.getChildrenNames(parent);
		List<String> values = new ArrayList<String>();
		for (String propertyName : propertyNames) {
			String value = getProperty(propertyName);
			if (value != null) {
				values.add(value);
			}
		}

		return values;
	}

	/**
	 * 返回所有属性名称.
	 * 
	 * @return 属性名称列表.
	 */
	public static List<String> getPropertyNames() {
		if (properties == null) {
			if (isSetupMode()) {
				return new ArrayList<String>();
			}
			properties = JawaProperties.getInstance();
		}
		return new ArrayList<String>(properties.getPropertyNames());
	}

	/**
	 * 设置一个属性值，如果不存在则创建一个.
	 * 
	 * @param name
	 *            属性名称.
	 * @param value
	 *            属性值.
	 */
	public static void setProperty(String name, String value) {
		if (properties == null) {
			if (isSetupMode()) {
				return;
			}
			properties = JawaProperties.getInstance();
		}
		properties.put(name, value);
	}

	/**
	 * 设置多个属性值.
	 * 
	 * @param propertyMap
	 *            多个属性值, key 值做为属性名称.
	 */
	public static void setProperties(Map<String, String> propertyMap) {
		if (properties == null) {
			if (isSetupMode()) {
				return;
			}
			properties = JawaProperties.getInstance();
		}

		properties.putAll(propertyMap);
	}

	/**
	 * 删除一个属性值.所有的子属性也将被删除.
	 * 
	 * @param name
	 *            属性名称.
	 */
	public static void deleteProperty(String name) {
		if (properties == null) {
			if (isSetupMode()) {
				return;
			}
			properties = JawaProperties.getInstance();
		}
		properties.remove(name);
	}

	/**
	 * 返回配置文件的名称.
	 * 
	 * @return 配置文件的名称
	 */
	public static String getConfigName() {
		return CONFIG_FILENAME;
	}

	/**
	 * 返回当前是否在安装模式.如果是 false 意味着安装已完成.
	 * 
	 * @return 如果是 true 代表正在安装.
	 */
	private static boolean isSetupMode() {
		if (Boolean.valueOf(JawaGlobals.getXMLProperty("setup"))) {
			return false;
		}
		// 检查数据库是否已配制完成，并正常运行.
		if (DbConnectionManager.getConnectionProvider() == null) {
			return true;
		}
		Connection con = null;
		PreparedStatement pstmt = null;
		try {
			con = DbConnectionManager.getConnection();
		} catch (SQLException e) {
			return true;
		} finally {
			DbConnectionManager.closeConnection(pstmt, con);
		}
		return false;
	}

	/**
	 * 加载系统的配制文件.
	 */
	private synchronized static void loadSetupProperties() {
		if (xmlProperties == null) {
			if (failedLoading) {
				failedLoading = true;
				Log.error("严重错误! 加载系统配制文件出错!");
			} else {
				try {
					xmlProperties = new XMLProperties(home + File.separator
							+ getConfigName());
				} catch (IOException ioe) {
					Log.error(ioe.getMessage(), ioe);
					failedLoading = true;
				}

				// 如果在配置目录下没有加载成功，那么在classPath 中尝试加载
				if (failedLoading) {
					try {
						URL resource = ClassUtils.getResource(getConfigName());
						if (null != resource) {
							xmlProperties = new XMLProperties(
									resource.getFile());
							failedLoading = false;
						}
					} catch (IOException ioe) {
						Log.error(ioe.getMessage(), ioe);
						failedLoading = true;
					}
				}

			}
		}
	}

}